package net.zoneland.zrdp.framework.config;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import org.java_websocket.util.NamedThreadFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import net.zoneland.zrdp.common.utils.Threads;

import java.util.concurrent.*;

/**
 * 线程池配置
 *
 * @author zonevue
 **/
@Configuration(proxyBeanMethods = false)
public class ThreadPoolConfig
{
    // 核心线程池大小
    private static final int CORE_POOL_SIZE = 8;

    // 最大可创建的线程数
    private static final int MAX_POOL_SIZE = 200;

    // 队列最大长度
    private static final int QUEUE_CAPACITY = 1000;

    // 线程池维护线程所允许的空闲时间
    private static final int KEEP_ALIVE_SECONDS = 300;

    @Bean(name = "threadPoolTaskExecutor")
    public AsyncListenableTaskExecutor threadPoolTaskExecutor() {
        final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
        // 设置拒绝策略为CallerRunsPolicy,表示当线程池没有空闲线程而队列已满时,新的任务由调用线程直接运行,不会被丢弃
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    /**
     * 单线程任务执行器
     * @return Executor
     */
    @Bean(name = "threadPoolTaskSequentialExecutor")
    public AsyncListenableTaskExecutor threadPoolTaskSequentialExecutor() {
        final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(1);
        executor.setCorePoolSize(1);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);

        // 设置拒绝策略为自定义的DiscardWithLogPolicy（抛弃任务输出日志）
        executor.setRejectedExecutionHandler(new DiscardWithLogPolicy());
        return executor;
    }

    /**
     * 执行周期性或定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        /* 默认 keepalivetime = 0立刻回收，maxsize = Integer.MAX_VALUE无限制 队列DelayedWorkQueue根据任务的预定执行时间（延迟时间）来决定队列中任务的顺序
           设置拒绝策略为CallerRunsPolicy,表示当线程池没有空闲线程而队列已满时,新的任务由调用线程直接运行,不会被丢弃
        */
        return new ScheduledThreadPoolExecutor(CORE_POOL_SIZE,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
    }

    /**
     * 统一通知线程池配置
     * @param corePoolSize 核心线程数
     * @param maximumPoolSize 最大线程数
     * @param keepAliveTime 线程空闲时间
     * @param queueCapacity 队列及队列最大任务数（最多可能会出现几千个任务同时进队列，正常情况下不会超过一万个任务）
     * @param threadNamePrefix 自定义的线程工厂及名称
     * @return 线程池服务对象
     */
    @Bean(name = "unimessageThreadExecutor")
    public ExecutorService unimessageThreadExecutor(
        @Value("${unimessage.threadPool.corePoolSize}") final int corePoolSize,
        @Value("${unimessage.threadPool.maximumPoolSize}") final int maximumPoolSize,
        @Value("${unimessage.threadPool.keepAliveTime}") final long keepAliveTime,
        @Value("${unimessage.threadPool.queueCapacity}") final int queueCapacity,
        @Value("${unimessage.threadPool.threadNamePrefix}") final String threadNamePrefix) {
        return new ThreadPoolExecutor(
            corePoolSize, // 核心线程数
            maximumPoolSize, // 最大线程数
            keepAliveTime, // 线程空闲时间
            TimeUnit.SECONDS, // 空闲时间的单位
            new LinkedBlockingQueue<>(queueCapacity), // 队列及队列最大任务数
            new NamedThreadFactory(threadNamePrefix), // 自定义的线程工厂及名称
            new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略
    }
}
